package com.ztl.leetcode.SwordFingerOffer;

/**
 * @author @ztl
 * @ClassName:NumericString_20
 * @Package:com.ztl.leetcode.SwordFingerOffer
 * @date 2023年05月11日 14:15
 * @description: 请实现一个函数用来判断字符串是否表示数值（包括整数和小数）。
 * @version: 1.0
 */
public class NumericString_20 {
    //只需要遍历一次字符串，时间复杂度O(n) 使用常数大小的空间，空间复杂度O(1)
    public boolean isNumber(String s) {
        //分析：数值字符串内合法字符只能是 " 0~9 E e + - . "
        //根据分析，首先排除包含非法字符的数字字符串
        //数字限制：无
        //'E e'限制：只能出现一次，前面必须包含数字，后面必须包含数字
        //'+ -'限制：只能出现在数字字符串第一位，以及'E e'后一位
        //'.'限制：只能出现1次，只能出现在'E e'前面
        //根据以上分析，只需要判断字符串中每个字符是否符合以上规则即可以判断是否是合法数字字符串
        //定义：num代表数字，dot代表小数点，e代表'E e'

        if(s==null || s.length()==0) return false;
        //定义状态：数字:num 小数点:dot 'E e':e
        boolean num=false;
        boolean dot=false;
        boolean e=false;

        //先去除字符串前后空格，转换成字符数组
        char[] c = s.trim().toCharArray();

        //for循环判断每个字符是否符合规则
        for(int i=0;i<c.length;i++){
            if(c[i]>='0' && c[i]<='9'){
                //数字没有限制，直接跳过，并且标记当前已经出现了数字
                num = true;
            } else if(c[i]=='.'){
                //遇到小数点，只能出现1次，只能出现在'E e'前面
                //dot||e成立，则代表小数点已经出现了或者e已经存在了，直接返回结果：false
                if(dot || e){
                    return false;
                }
                dot = true;
            }else if(c[i]=='e' || c[i]=='E'){
                //'E e'限制：只能出现一次，前面必须包含数字，后面必须包含数字
                if(!num || e){
                    return false;
                }
                e = true;
                //把数字标记为false，后续如果没出现数字则最后返回false
                num = false;
            }else if(c[i]=='-' || c[i]=='+'){
                //'+ -'限制：只能出现在数字字符串第一位，以及'E e'后一位
                if(i!=0 && c[i-1]!='e' && c[i-1]!='E'){
                    return false;
                }
            }else{
                return false;
            }
        }

        //这里不直接返回true是因为防止e后面没有出现数字
        return num;
    }
}
